abs($x2 - $x1); if($steep) { list($x1, $y1) = array($y1, $x1); list($x2, $y2) = array($y2, $x2); } if($x2 < $x1) { list($x1, $x2) = array($x2, $x1); list($y1, $y2) = array($y2, $y1); } $dx = $x2 - $x1; $dy = $y2 - $y1; if(!$dx && !$dy) { return false; } $gradient = $dy / $dx; // first endpoint $xend = (int)($x1 + 0.5); $yend = $y1 + $gradient * ($xend - $x1); $xgap = self::wu_rfpart($x1 + 0.5); $xpxl1 = $xend; $ypxl1 = intval($yend); self::wu_plot($im, $xpxl1, $ypxl1, self::wu_rfpart($yend) * $xgap, $color, $steep, $thickness); self::wu_plot($im, $xpxl1, $ypxl1 + 1, self::wu_fpart($yend) * $xgap, $color, $steep, $thickness); $intery = $yend + $gradient; // first y-intersection for the main loop // second endpoint $xend = (int)($x2 + 0.5); $yend = $y2 + $gradient * ($xend - $x2); $xgap = self::wu_fpart($x2 + 0.5); $xpxl2 = $xend; $ypxl2 = intval($yend); self::wu_plot($im, $xpxl2, $ypxl2, self::wu_rfpart($yend) * $xgap, $color, $steep, $thickness); self::wu_plot($im, $xpxl2, $ypxl2 + 1, self::wu_fpart($yend) * $xgap, $color, $steep, $thickness); for($x = $xpxl1 + 1; $x < $xpxl2; ++$x) { self::wu_plot($im, $x, (int)$intery, self::wu_rfpart($intery), $color, $steep, $thickness); self::wu_plot($im, $x, (int)$intery + 1, self::wu_fpart($intery), $color, $steep, $thickness); $intery += $gradient; } return true; } protected function wu_plot(&$im, $x, $y, $c, $color, $swap = false, $thickness = 1) { $color = ($color & 0x80FFFFFF) | (((1-$c) * 127) << 24); if($swap) { list($x, $y) = array($y, $x); } if($thickness > 1) { imagefilledellipse($im, $x, $y, $thickness, $thickness, $color); } else { imagesetpixel($im, $x, $y, $color); } } protected function wu_fpart($x) { return (float)($x - (int)$x); } protected function wu_rfpart($x) { return 1 - self::wu_fpart($x); } // From Xavier Gouchet @ http://www.xgouchet.fr/bezier.php?language=en // removed drawing of points and added thickness function bezier4($image, $x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4, $color, $steps = 3, $thickness = 1) { if($steps > 1) { $inc = 1 / $steps; $ox = $x1; $oy = $y1; for($i = 1; $i < $steps; $i++) { // get factors $scale = $i*$inc; $invscale = 1 - $scale; // get J,K,L values $jx = ($invscale*$x1) + ($scale*$x2); $jy = ($invscale*$y1) + ($scale*$y2); $kx = ($invscale*$x2) + ($scale*$x3); $ky = ($invscale*$y2) + ($scale*$y3); $lx = ($invscale*$x3) + ($scale*$x4); $ly = ($invscale*$y3) + ($scale*$y4); // get M, N and P values $mx = ($invscale*$jx) + ($scale*$kx); $my = ($invscale*$jy) + ($scale*$ky); $nx = ($invscale*$kx) + ($scale*$lx); $ny = ($invscale*$ky) + ($scale*$ly); $px = ($invscale*$mx) + ($scale*$nx); $py = ($invscale*$my) + ($scale*$ny); // draw ''arc'' $res = self::line($image, $ox, $oy, $px, $py, $color, $thickness); // decal o->p $ox = $px; $oy = $py; } // finalize curve self::line($image, $ox, $oy, $x4, $y4, $color, $thickness); } else { if($steps == 1) { // one step means only one line self::line($image, $x1, $y1, $x4, $y4, $color); } } return true; } // got this function from a post by Alexander Gavazov // on http://us3.php.net/manual/en/function.imagettfbbox.php function calculate_text_box($size, $angle, $font, $text) { $box = imagettfbbox($size, $angle, $font, $text); $min_x = min(array($box[0], $box[2], $box[4], $box[6])); $max_x = max(array($box[0], $box[2], $box[4], $box[6])); $min_y = min(array($box[1], $box[3], $box[5], $box[7])); $max_y = max(array($box[1], $box[3], $box[5], $box[7])); return array( 'left' => ($min_x >= -1) ? -abs($min_x + 1) : abs($min_x + 2), 'top' => abs($min_y), 'width' => $max_x - $min_x, 'height' => $max_y - $min_y, 'box' => $box ); } } class Captcha_Generator { protected $im; public $width = 120; public $height = 45; // text color, number or array to randomize public $color = 0x0; // array(0x9c0032, 0x69c9ff, 0xed00d5, 0x3a60ff, 0xff0328, 0x0017e7, 0xff1ffe, 0x17ccff); // background color, number or array to randomize public $bgcolor = 0xffffee; // array(0xb6ffc8, 0x69c9ff, 0xf7ffb4, 0xfcd8ff, 0xffe2dd); // image format. jpeg, gif, or png public $format = 'png'; // jpeg quality, only if format is jpeg public $jpeg_quality = 80; // length of random string public $string_length = 4; // string of characters to pick from. Below is the default with a few characters removed (like lower I and 1) public $characters = 'ABCDEFGHJKMNOPQRSTUVWXYZ23456789'; public $font = 'book.ttf'; public $font_path = ''; public $font_size = 14; public $font_spacing = -1; // number or array of two numbers to randomize public $font_angle = array(-10, 10); // use the built-in font if FreeType is not available public $basic_font = 5; // warp the image following a sine wave. // Play around with the amplitude and period for best result depending on the image size. public $wavify = true; // amplitude public $wave_amp = 10; // period public $wave_period = 20; // draw a random bezier line across the image public $draw_bezier = 1; // bezier line thickness public $bezier_line_thickness = 1; function __construct($options = array()) { foreach($options as $k => $v) { $this->$k = $v; } } // write to path or stdout function output($path = null) { if(!$this->im) { $this->render_image(); } if(!$path) { header("Content-type: image/{$this->format}"); } $fn = "image" . $this->format; if($this->format == 'jpeg') { $fn($this->im, $path, $this->jpeg_quality); } else { $fn($this->im, $path); } } // render $text, usually the stored code. Usually not called function write($text = null) { if(!$text) { $text = $this->controller->Session->get('captcha_code'); } $this->im = imagecreatetruecolor($this->width, $this->height); imagefill($this->im, 0, 0, is_array($this->bgcolor) ? $this->bgcolor[array_rand($this->bgcolor, 1)]: $this->bgcolor); imagesavealpha($this->im, true); imagealphablending($this->im, true); $text_len = strlen($text); $color = is_array($this->color) ? $this->color[array_rand($this->color, 1)]: $this->color; if(function_exists('imagettftext')) { $letter_spacing = 30; $font = $this->font_path . $this->font; $write = array(); $text_width = 0; for($i = 0; $i < $text_len; ++$i) { $angle = is_array($this->font_angle) ? mt_rand($this->font_angle[0], $this->font_angle[1]) : $this->font_angle; $box = GD_Helper::calculate_text_box($this->font_size, $angle, $font, $text{$i}); $write[] = array($this->font_size, $angle, $box['width'] + $this->font_spacing, $box['height']); $text_width += $box['width'] + $this->font_spacing; } $x = mt_rand(0, $this->width - $text_width); //($this->width - $text_width) / 2; for($i = 0; $i < $text_len; ++$i) { list($size, $angle, $width, $height) = $write[$i]; $y = ($this->height + $height) / 2; imagettftext($this->im, $this->font_size, $angle, $x, $y, $color, $font, $text{$i}); $x += $width; } } else { $font = $this->basic_font; $x = floor((imagesx($this->im) - (imagefontwidth($font) * $text_len)) / 2); $y = floor((imagesy($this->im) - imagefontheight($font)) / 2); imagestring($this->im, $font, $x, $y, $text, $color); } for($i = 0; $i < $this->draw_bezier; ++$i) { $q = $this->width / 4; GD_Helper::bezier4($this->im, 0, mt_rand(0, $this->height), mt_rand(0, $q), mt_rand(0, $this->height), mt_rand(3*$q, $this->width), mt_rand(0, $this->height), $this->width, mt_rand(0, $this->height), $color, 25, $this->bezier_line_thickness ); } if($this->wavify) { GD_Helper::wave_region($this->im, 0, 0, $this->width, $this->height, false, $this->wave_amp, $this->wave_period, mt_rand(-10, 10), 4 ); } } function destroy() { imagedestroy($this->im); $this->im = null; } protected function _get_random_string($len = 5, $p = '') { $c = $this->characters; $x = strlen($c) - 1; for($i = 0; $i < $len; $i++) { $p .= $c{mt_rand(0,$x)}; } return $p; } } ?>